Android插件化開發之OpenAtlas中四大組件與Application功能的驗證

使用OpenAtlas進行插件化開發,插件的開發幾乎可以按照正常程序的開發流程進行,無需添加額外的東西。為瞭驗證四大組件是否能夠正常工作,這裡編寫一個插件,驗證其功能。除瞭四大組件外,大多數應用還有Application類。該類我們也需要進行驗證。

首先新建一個模塊,按照正常流程進行開發。新建Application類,為瞭方便起見,所有驗證都使用日志輸出形式。

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, Plugin named Component has init!);
        Log.e(TAG, ===Plugin Application is=== + this);
    }
}

Activity的驗證不再重復,前面幾篇文章都是以Activity或者Fragment為基礎的,有興趣可以轉到相關鏈接。
– Android插件化開發之OpenAtlas初體驗
– Android插件化開發之OpenAtlas生成插件信息列表
– Android插件化開發之OpenAtlas資源打包工具補丁aapt的編譯
– Android插件化開發之OpenAtlas插件適配
– Android插件化開發之解決OpenAtlas組件在宿主的註冊問題

接下來驗證Service,新建一個Service,清單文件的註冊放到最後。

public class ComponentService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, ===Plugin Service onCreate===);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, ===Plugin Service onBind===);
        return new BinderImpl();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, ===Plugin Service onUnbind===);
        return super.onUnbind(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, ===Plugin Service onStartCommand===);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, ===Plugin Service onDestroy===);
    }
    class BinderImpl extends Binder{
        BinderImpl getService(){
            return BinderImpl.this;
        }
        void testMethod(){
            Log.e(TAG,BinderImpl testMethod);
        }
    }

}

然後是ContentProvider。都是空實現,隻是輸出日志。

public class ComponentContentProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        Log.e(TAG,ComponentContentProvider onCreate);
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.e(TAG,ComponentContentProvider query:+Arrays.asList(projection)+ +Arrays.asList(selectionArgs)+ +selectionArgs+ +sortOrder);
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        Log.e(TAG,ComponentContentProvider getType);
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.e(TAG,ComponentContentProvider insert:+values);
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.e(TAG,ComponentContentProvider delete:+selection+ + Arrays.asList(selectionArgs));
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        Log.e(TAG,ComponentContentProvider update:+values+ +selection+ +Arrays.asList(selectionArgs));
        return 0;
    }
}

以及廣播,廣播有兩種,一種是靜態廣播,需要在清單文件中註冊,一種是動態廣播,代碼註冊

public class ComponentBroadcast extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, ===Plugin BroadcastReceiver onReceive===);
    }
}
public class ComponentBroadcastDynamic extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, ===Plugin ComponentBroadcastDynamic onReceive===);
    }
}

最後在插件的清單文件中進行這些組件的註冊。



    
        
            
                

                
            
        


        
        

        
            
                
            
        

        
    


 data-snippet-id=ext.3d490ef9fc184d473a2aa363079dd76e data-snippet-saved=false data-codota-status=done>


    
        
            
                

                
            
        


        
        

        
            
                
            
        

        
    


編寫一個Activity驗證所有功能,Activity的佈局不再貼出,整個佈局就是Button組成的一個佈局。


public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e(TAG, ===Plugin MainActivity is=== + this);


        findViewById(R.id.start_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, ComponentService.class);
                startService(intent);

            }
        });

        findViewById(R.id.stop_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, ComponentService.class);
                stopService(intent);

            }
        });

        final ServiceConnection conn = new ServiceConnection() {

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, onServiceDisconnected());
            }

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.i(TAG, onServiceConnected());
                ComponentService.BinderImpl binder = (ComponentService.BinderImpl) service;
                ComponentService.BinderImpl bindService = binder.getService();
                bindService.testMethod();

            }
        };
        findViewById(R.id.bind_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, ComponentService.class);
                Log.i(TAG, bindService());
                bindService(intent, conn, Context.BIND_AUTO_CREATE);
            }
        });

        findViewById(R.id.unbind_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(conn);
            }
        });
        findViewById(R.id.broadcast).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(cn.edu.zafu.component);
                sendBroadcast(intent);

            }
        });
        final ComponentBroadcastDynamic componentBroadcastDynamic = new ComponentBroadcastDynamic();
        findViewById(R.id.register_broadcast).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                IntentFilter intentFilter = new IntentFilter();
                intentFilter.addAction(cn.edu.zafu.component.ComponentBroadcastDynamic);
                registerReceiver(componentBroadcastDynamic, intentFilter);
                Log.e(TAG,ComponentBroadcastDynamic registerReceiver);

            }
        });
        findViewById(R.id.send_broadcast_dynamic).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent();
                intent.setAction(cn.edu.zafu.component.ComponentBroadcastDynamic);
                sendBroadcast(intent);

            }
        });
        findViewById(R.id.unregister_broadcast).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, ComponentBroadcastDynamic unregisterReceiver);
                try{
                    unregisterReceiver(componentBroadcastDynamic);
                }catch (Exception e){
                    Log.e(TAG, ComponentBroadcastDynamic has already unregister);
                }


            }
        });

        findViewById(R.id.contentprovider_insert).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String authorities = cn.edu.zafu.component.ComponentContentProvider;
                Uri CONTENT_URI = Uri.parse(content:// + authorities + /test);
                ContentValues values = new ContentValues();
                values.put(title, title11111111);
                Uri uri =MainActivity.this.getContentResolver().insert(CONTENT_URI, values);

            }
        });

        findViewById(R.id.contentprovider_delete).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String authorities = cn.edu.zafu.component.ComponentContentProvider;
                Uri CONTENT_URI = Uri.parse(content:// + authorities + /test);
                int result =MainActivity.this.getContentResolver().delete(CONTENT_URI, title=?, new String[]{title11111111});
                Log.e(TAG,=====+result);
            }
        });


        findViewById(R.id.contentprovider_update).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String authorities = cn.edu.zafu.component.ComponentContentProvider;
                Uri CONTENT_URI = Uri.parse(content:// + authorities + /test);

                ContentValues values = new ContentValues();
                values.put(title, title11111111);
                int result =MainActivity.this.getContentResolver().update(CONTENT_URI, values, id=?, new String[]{1});
                Log.e(TAG, ===== + result);
            }
        });

        findViewById(R.id.contentprovider_query).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Log.e(TAG,no impl);
            }
        });
    }

}

按照這篇文章 Android插件化開發之OpenAtlas插件適配 進行適配工作。在build.gradle中加入flavors,讓其同時支持單獨運行以及插件運行。

productFlavors {
        alone{
        }
        openatlas {
            versionName 1.00x23
        }
    }

這時候我們選擇脫離插件模式運行,如圖
這裡寫圖片描述

運行成功後,我們從上到下依次點擊按鈕,
這裡寫圖片描述
運行後查看日志輸出。

這裡寫圖片描述

可以看到單獨運行的時候所有功能都能正常運行。vcD4NCjxwPtXiyrG68s7Sw8fSqr2rxuTSxtaytb3L3tb31tCjrLXa0ruyvb7Nyse9q8fltaXOxLz+1tC1xMn5w/e4tNbGtb3L3tb31tCjrLKi1/bKyrWx0N64xKGjPC9wPg0KPHByZSBjbGFzcz0=”brush:java;”>

data-snippet-id=ext.62ad36c5281b478991281e615e074259 data-snippet-saved=false data-codota-status=done>

從以上聲明可以看出,我們的provider的聲明發生瞭變化,其實原因也很簡單,這裡我們做瞭一個橋。
而Provider需要做一些特殊處理,因為ContentProvider在Application onCreate之前初始化,因此做一個橋,告訴系統這個ContentProvider初始化完畢,都可以用瞭,實際上還沒完成,隻是一個空實現,當需要的類能加載的時候對正常的類進行實例化。

那麼這個橋的實現是怎麼樣的呢,也很簡單,繼承ProviderProxy類,調用父類構造函數即可。調用構造函數傳遞的參數為真正的Provider的全類名。也就是原來插件的清單文件中的類名。

public class ComponentProviderBridge extends ProviderProxy{

    public ComponentProviderBridge() {
        super(cn.edu.zafu.component.ComponentContentProvider);
    }
}

而宿主中的清單文件的類名對應變為橋的全類名。完成瞭以上工作後就可以將build方式修改為openAtlas的瞭。如圖。

這裡寫圖片描述

生成插件後還要生成插件信息列表,按照之前的文章進行生成即可。

這裡寫圖片描述
然後在宿主中調用插件的入口Activity

 findViewById(R.id.component).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClassName(MainActivity.this, cn.edu.zafu.component.MainActivity);
                startActivity(intent);
            }
        });

運行後點擊對應按鈕進入插件,並按之前的操作那樣,從上到下依次點擊所有按鈕測試功能。查看日志輸出。

這裡寫圖片描述

我們會發現和單獨運行完全一樣。

最後呢,還有一個細節問題需要自己註意一下,如果你在宿主中單獨使用反射調用插件中的Fragment,如果不經過插件的四大組件,比如Activity,那麼插件的Application將不會被調用,這裡提供一種解決方法,就是手動進行Bundle的啟動,在反射前進行調用,代碼如下

BundleImpl bundle = (BundleImpl)Atlas.getInstance().getBundle(xxxxxxxx);
bundle.startBundle();

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *