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===");
    }
}

最后在插件的清单文件中进行这些组件的注册。

<?xml version="1.0" encoding="utf-8"?>
<manifest package="cn.edu.zafu.component"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:name="cn.edu.zafu.component.App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>


        <service
            android:name="cn.edu.zafu.component.ComponentService"
            android:exported="false">
        </service>

        <receiver android:name="cn.edu.zafu.component.ComponentBroadcast">
            <intent-filter>
                <action
                    android:name="cn.edu.zafu.component"/>
            </intent-filter>
        </receiver>

        <provider
            android:name="cn.edu.zafu.component.ComponentContentProvider"
            android:authorities="cn.edu.zafu.component.ComponentContentProvider"/>
    </application>

</manifest>

编写一个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"
        }
    }

这时候我们选择脱离插件模式运行,如图
这里写图片描述

运行成功后,我们从上到下依次点击按钮,
这里写图片描述
运行后查看日志输出。

这里写图片描述

可以看到单独运行的时候所有功能都能正常运行。

这时候我们要将其移植到宿主中,第一步就是将清单文件中的声明复制到宿主中,并做适当修改。

<activity
            android:name="cn.edu.zafu.component.MainActivity"
          >
        </activity>
        <service
            android:name="cn.edu.zafu.component.ComponentService"
            android:exported="false">
        </service>
        <receiver android:name="cn.edu.zafu.component.ComponentBroadcast">
            <intent-filter>
                <action
                    android:name="cn.edu.zafu.component"/>
            </intent-filter>
        </receiver>
        <provider
            android:name="cn.edu.zafu.atlasdemo.provider.ComponentProviderBridge"
            android:authorities="cn.edu.zafu.component.ComponentContentProvider"/>

从以上声明可以看出,我们的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();

最后上源码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值