2021年1月20日 星期三

Android multiple modular developing

      My previous app was an android banking app. It is a neo bank. And it got tons of features relative to different financial products. I establish the basic architecture with mvvm + navigation component . One activity with fragments. Everything works perfectly thanks for jetpack. Easy to refactor and developing based on this architecture. Also a lot of interesting details with di, motionlayout, paging, navigation, coroutine, viewmodel. Again still work perfectly. 

     What makes this app more special is the multiple modular developing, we manage 8 engineers at the whole time, 10+ with out-source agencies developers. How do we avoid code conflicts and make codes more clear. I introduce the concept of the multiple modular developing. There are some modern software companies are already using them and share their experience on the internet. (#1)

     Basically design your app separated by features. and I believe those features are in 
distinguish UI layer in your app (If you are saying it's not, I serious doubt the UX and everything on that app). For example your app is a tab-layout, each tab represented a feature. Then you can separate your app into different features. The whole launch app was in the App module and each tabs' codes are lying each `Feature #` modules. And move the whole common codes(string res, network, database, utilities..etc) into the bottom layers of modules. So the basic diagram would looks like this.

     



 
     Furthermore as your app growth, the diagram can be designed more and more complicated like this






    There are pros and one con of this developing methods. 

  1. Good for you team members to know where they should focus on and totally ignore other teams simultaneously developing codes.
  2. All the features are reusable, disposable based on requirement. As long as you manager your project dependencies clean and compact. 
  3. Google play now support `Dynamic Feature` based on this architecture, maybe someday your pm has crazy thoughts on this feature.
  4. Test would be easier and single launch is possible if you design and mock it right.
     The con is. As the modules growth, the required build time of android studio takes longer. And there are some methods to avoid this.
  1. Use di to inject blank UI so other modules won't be involved during builds.
  2. Use local maven to store the local build of each modules. They won't be rebuild anymore. Only focus and rebuild on the module you work on.(#2)
     Here I shared a simple app demonstrate such a concept.
https://github.com/hydrated/CurrencySample


Appendix

#2



2020年6月10日 星期三

ViewParentCompat of NestedScrollingParent2 NestedScrollingChild2

日前在做MotionLayout發現example可以輕易的將scrolling event往外傳,然後MotionLayout接收後就自動Parse完畢trigger transition。覺得神奇。

https://github.com/android/views-widgets-samples/blob/master/ConstraintLayoutExamples/motionlayout/src/main/java/com/google/androidstudio/motionlayoutexample/utils/TouchFrameLayout.java


基本上Android開始把可以Scrolling的View用一個叫ViewParentCompat的東西作協調器,然後可以透過viewparentcompat中定義的parent child來互相傳達scrolling event

public class RecyclerView extends ViewGroup implements ScrollingView
        ,NestedScrollingChild2, NestedScrollingChild3 {

從RecyclerView中,是個child的角色,然後自己scrolling時,往外丟出scrolling event

RecyclerView.class
@Overridepublic boolean dispatchNestedPreScroll(int dx, int dy, 
        int[] consumed, int[] offsetInWindow, int type) {

    return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,            type);}

ViewParentCompat如果是個Parent的角色,自然就會接收到scrolling event

ViewParentCompat.class
if (parent instanceof NestedScrollingParent2) {
    // First try the NestedScrollingParent2 API    
    ((NestedScrollingParent2) parent).onNestedPreScroll(target, dx,
         dy, consumed, type);}


那麼Parent就會收到callback onNestedPreScroll(xxxx
@Overridepublic void onNestedPreScroll(@NonNull View target, int dx,
        int dy, @NonNull int[] consumed, int type) {
    getMotionLayout().onNestedPreScroll(target, dx, dy, 
        consumed, type);}

接著把所有callback event導入MotionLayout,看起來就可以吳鳳接軌。


Beautiful, isn't it.








2019年1月22日 星期二

Slow firebase querying

Regarding your follow-up question, I would like to add more context about load spike.
The percentage shown roughly represents the max I/O operation per second that your database can handle, with the caveat that it also depends on the actual size of the operation events to what sort of throughput you get. During this peak, some of your operations will not be executed on the time you expect them to trigger. In other words, when it reaches 100%, your database will start to queue up additional requests in a backlog until they can be executed. This surfaces as latency and in severe cases (if you stay at 100% for an extended period of time) the DB can even appear unresponsive (although ultimately the requests get served, just very slowly). The greater the load percentage your project is incurring, the greater the latency you will be experiencing.

These load peaks usually caused by the following (which also applies to your database operations inside Cloud Functions):
  • Large individual reads, typically a value listen high up in the tree
  • Accessing specific node on a huge collection
  • Queries against large collections
  • Too frequent writes
  • Deleting or exporting large data
If I may, you could check the following suggestions that may help:
  • Archive or delete nodes in any lists that contain more than 100k items (or items that are queried hundreds of times per second with more than 10k nodes). This is true when querying over huge collections even if the limit is set to minimum, e.g. using limitToFirst(10). Firebase Database needs to first traverse all the data and build index in memory and only then fetch the results.
  • Ensure queries are efficient and only retrieving what people can consume (i.e. fetch 20 records initially and get more as needed, don't try to preload 200)
  • If you have a backup process or server script downloading large chunks of your DB daily/hourly/etc, switch to our automated backups and use that for analysis/backups/et al
  • Throttle the number of bytes being deleted at once by navigating deeper to your nodes, and delete it in chunks to avoid unresponsiveness.
  • For write operations:
  • If these are coming from many clients, we would suggest to randomize the time of operation.
  • If these are coming from your backend, we would advise to smear these requests over longer period of time.

2018年12月23日 星期日

Fragment消失時

1. 使用的viewpager or something 使用錯誤的getFragmentManager() or getChildFragmentManager()

2. 該頁Fragment layout可能有異常,嘗試使用ViewGroup包妥善

2018年12月4日 星期二

Android Focus一些概念

android提供了一些焦点相关的属性,在现有的框架层下通过设置View的属性来获得焦点:
  • android:focusable:设置一个控件能否获得焦点
  • android:nextFocusDown:(当按下键时)下一个获得焦点的控件
  • android:nextFocusDown:(当按下键时)下一个获得焦点的控件
  • android:nextFocusLeft:(当按下键时)下一个获得焦点的控件
  • android:nextFocusRight:(当按下键时)下一个获得焦点的控 **注意:**如果按下某个方向键时,想让焦点停留在自身,可以使用android:nextFocusRight:"@null"或者android:nextFocusRight:"@id/自身id"
這只有在對view是有效的,對viewgroup等類是無效的 原因在於

private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {
// check for user specified next focus
View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
View cycleCheck = userSetNextFocus;
boolean cycleStep = true; // we want the first toggle to yield false
while (userSetNextFocus != null) {
if (userSetNextFocus.isFocusable()
&& userSetNextFocus.getVisibility() == View.VISIBLE
&& (!userSetNextFocus.isInTouchMode()
|| userSetNextFocus.isFocusableInTouchMode())) {
return userSetNextFocus;
}
userSetNextFocus = userSetNextFocus.findUserSetNextFocus(root, direction);
if (cycleStep = !cycleStep) {
cycleCheck = cycleCheck.findUserSetNextFocus(root, direction);
if (cycleCheck == userSetNextFocus) {
// found a cycle, user-specified focus forms a loop and none of the views
// are currently focusable.
break;
}
}
}
return null;
} 他在搜尋usersetnextfocus是用該focus view去找的 viewgroup自然對於該function就無作用。


一般來說focusSearch是focus主要的來源,如果都不override他就是一路找到decorView?或許吧,使用FocusSearchInstance來找,那個Util會把子Views focusable的全找出來作排序用rect的方式去找最靠近的focus, 除非你有指定id. 原生的四大主件LinearLayout RelativeLayout ...全都沒有複寫這method. 只有Leanback library的元件才有,所以客製化的頁面UI幾乎全都要自己override這method來控制focus。至少我認為是這樣。

@Overridepublic View focusSearch(View focused, int direction) {
    View returnView = null;
    ViewParent trace = focused.getParent();
    while (trace != null) {
        if (trace instanceof SlidingTabLayout) {
            returnView = FocusFinder.getInstance().findNextFocus((ViewGroup) trace, focused, direction);            break;        } else if (trace instanceof ViewPager) {
            returnView = FocusFinder.getInstance().findNextFocus((ViewGroup) trace, focused, direction);            break;        }
        trace = trace.getParent();    }

    if (trace instanceof SlidingTabLayout && direction == View.FOCUS_DOWN) {
        return pager;    }

    if (trace instanceof ViewPager && direction == View.FOCUS_UP) {
        return tabs.getSelectedView();    }

    return returnView == null ? super.focusSearch(focused, direction) : returnView;}

以上是一些客製化的範例。

2018年6月22日 星期五

The concept of using Google api using javascript.

There are 2 stand ways to call the google-apis,


  1. Use oauth client
  2. Use oauth server-to server applications


Concept.
For 1. The oauth flows are pretty standard for any oauth2 applications.
First you ask permission to the user that granted you to access the calendar.
Then there is a access_token and a refresh_token.
So that you can access user’s calendar via that access_token.

For 2. This is under the G-suite admin authority to manipulating G-suite users data
using a delegated account.
Create a service account and let that account has access to the data you want to manipulate.



The flow charts are
For 1.
Get token using oauth2  => Initialize an google-apis client with token and scopes =>
Use client to send the api

For 2.
Create Service Account Client in the google console =>
Create Security in G-suite app using G-suite admin account =>
JWT can make request to access G-suite users


There are 3  approach ways.


  1. Using googleapis in could-functions in node.js.


Implementation details.


For 2.

  1. To create a service account as a delegate.
  2. Use the credential to authorize the JWTclient.
  3. Add that service account to the calendar you want to manipulate.
  4. Manipulate calendar using AWT like google apis.


Reference of creating google calendar, setup gcp project.

2017年11月14日 星期二

Android Interacting with Other Apps using Messenger or AIDL

在Linux階層上的IPC,Android用了iBinder來包裝之間溝通的管道。iBinder有兩種形式可以傳遞資料。預備知識是使用android的service並且exported,可使其他application能夠喚起該service。


第一種是用Messenger封裝起來,使用Message來傳遞,開啟Service後,建立一個Messenger instance,使用Messenger的getBinder提出傳給service,而client也是透過取得的binder來創建Messenger來溝通。並且可以使用Messenger.replyTo來建立一個收訊息的接口來跟遠端service溝通。溝通的單位是bundle。

第二種是使用Android Interface Definition Language,簡單的說就是iBinder中定好溝通的規則,也就是functions。在server(service端)建立好iInterface的aidl實作的binder,在service中回傳該binder,然後在client端中也是用aidl檔承接轉型該ibinder至iInterface,雙方就透過iInterface中定義好的functions溝通。





一些概念解釋:


iBinder就是android中建立在ipc上的java層,用來使用ipc來溝通的。

Messenger其實就是封裝iBinder,使用Messenger類來透過ibinder來溝通。

AIDL則是在ibinder層先定義好傳輸資料,接著雙方用該接口來互動。該檔案需在sdk中編譯,並且對應相同的pacakge name,比如說你aidl package name是com.handsome.hydra你就得放在src/main/aidl/com/handsome/hydra/的資料夾下放IInterface.aidl,此後sdk編譯好後你才能使用IInterface.stub方法。

若你有使用ipc shared memory, message queue的經驗,基本上java那層就是封裝過的實作層,讓你用他們定義好的資料傳輸方式來使用ipc就是了。