http://www.cnblogs.com/wotakuc/archive/2013/03/27/2984423.html
刚整理完的文档,顺便园子发个分享。因工作繁忙,还是有不少方面无法深入测试,欢迎各位回帖提出意见~
在Android 系统中,所有安装到系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程序之间建立信任关系,。这个数字证书并不需要权威的数字证书签名机构认证,它只是用来让应用程序包自我认证的。
调试时,ADT会自动的使用debug密钥为应用程序签名。debug密钥是一个名为debug.keystore的文件,它的位置:系统盘符:/Documents and Settings/XXX/.android/debug.keystore “XXX”对应于windows操作系统用户名。
主要涉及工具有三个,keytool、jarsigner和zipalign
1)keytool:生成数字证书,即密钥,也就是上面说到的扩展名为.keystore的那类文件;
2)jarsigner:使用数字证书给apk文件签名;
3)zipalign:对签名后的apk进行优化,提高与Android系统交互的效率(Android SDK1.6版本开始包含此工具)
通常,可直接通过Ecplise的adt插件提供的功能来签名。(详细可见网络其他资源,本文主要讨论shareUserId)
Android给每个APK进程分配一个单独的空间,manifest中的userid就是对应一个分配的Linux用户ID,并且为它创建一个沙箱,以防止影响其他应用程序(或者其他应用程序影响它)。用户ID 在应用程序安装到设备中时被分配,并且在这个设备中保持它的永久性。
通常,不同的APK会具有不同的userId,因此运行时属于不同的进程中,而不同进程中的资源是不共享的,在保障了程序运行的稳定。然后在有些时候,我们自己开发了多个APK并且需要他们之间互相共享资源,那么就需要通过设置shareUserId来实现这一目的。
通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中.所以默认就是可以互相访问任意数据. 也可以配置成运行成不同的进程, 同时可以访问其他APK的数据目录下的数据库和文件.就像访问本程序的数据一样。
在需要共享资源的项目的每个AndroidMainfest.xml中添加shareuserId的标签。
android:sharedUserId="com.example"
id名自由设置,但必须保证每个项目都使用了相同的sharedUserId。一个mainfest只能有一个Shareuserid标签。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.shareusertesta" android:versionCode="1" android:versionName="1.0" android:sharedUserId="com.example">
每个安装的程序都会根据自己的包名在手机文件系统的data\data\your package\建立一个文件夹(需要su权限才能看见),用于存储程序相关的数据。
在代码中,我们通过context操作一些IO资源时,相关文件都在此路径的相应文件夹中。比如默认不设置外部路径的文件、DB等等。
正常情况下,不同的apk无法互相访问对应的app文件夹。但通过设置相同的shareUserId后,就可以互相访问了。代码如下。
//程序A: public class MainActivityA extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.textView1); WriteSettings(this, "123"); } public void WriteSettings(Context context, String data) { FileOutputStream fOut = null; OutputStreamWriter osw = null; try { //默认建立在data/data/xxx/file/ fOut = openFileOutput("settings.dat", MODE_PRIVATE); osw = new OutputStreamWriter(fOut); osw.write(data); osw.flush(); Toast.makeText(context, "Settings saved", Toast.LENGTH_SHORT) .show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(context, "Settings not saved", Toast.LENGTH_SHORT) .show(); } finally { try { osw.close(); fOut.close(); } catch (IOException e) { e.printStackTrace(); } } }
//程序B: public class MainActivityB extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) this.findViewById(R.id.textView1); try { //获取程序A的context Context ctx = this.createPackageContext( "com.example.shareusertesta", Context.CONTEXT_IGNORE_SECURITY); String msg = ReadSettings(ctxDealFile); Toast.makeText(this, "DealFile2 Settings read" + msg, Toast.LENGTH_SHORT).show(); WriteSettings(ctx, "deal file2 write"); } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public String ReadSettings(Context context) { FileInputStream fIn = null; InputStreamReader isr = null; char[] inputBuffer = new char[255]; String data = null; try { //此处调用并没有区别,但context此时是从程序A里面获取的 fIn = context.openFileInput("settings.dat"); isr = new InputStreamReader(fIn); isr.read(inputBuffer); data = new String(inputBuffer); textView.setText(data); Toast.makeText(context, "Settings read", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(context, "Settings not read", Toast.LENGTH_SHORT) .show(); } finally { try { isr.close(); fIn.close(); } catch (IOException e) { e.printStackTrace(); } } return data; } public void WriteSettings(Context context, String data) { FileOutputStream fOut = null; OutputStreamWriter osw = null; try { fOut = context.openFileOutput("settings.dat", MODE_PRIVATE); //此处调用并没有区别,但context此时是从程序A里面获取的 osw = new OutputStreamWriter(fOut); osw.write(data); osw.flush(); Toast.makeText(context, "Settings saved", Toast.LENGTH_SHORT) .show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(context, "Settings not saved", Toast.LENGTH_SHORT) .show(); } finally { try { osw.close(); fOut.close(); } catch (IOException e) { e.printStackTrace(); } } } }
如果A和B的mainfest中设置了相同的shareuserId,那么B的read函数就能正确读取A写入的内容。否则,B无法获取该文件IO。
通过这种方式,两个程序之间不需要代码层级的引用。之间的约束是,B需要知道A的file下面存在“settings.dat”这个文件以及B需要知道A的package的name。
通过shareuserId共享,我们可获取到程序A的context。因此,我们就可以通过context来获取程序A对应的各种资源。比较常用的就是Raw资源的获取,如一些软件的apk皮肤包就是采用了这种技术,将主程序和皮肤资源包分在两个apk中。
获取Resources很简单,在程序A和B的mainfest中设置好相同的shareuserId后,通过createPackageContext获取context即可。之后就和原来的方式一样,通过getResources函数获取各种资源,只是此时的context环境是目标APP的context环境。
//B中调用 Context friendContext = this.createPackageContext( "com.example.shareusertesta",Context.CONTEXT_IGNORE_SECURITY); //在B中获取A的各种资源 friendContext.getResources().getString(id); friendContext.getResources().getDrawable(id);
可看见,与一般获取资源的方式并没有区别,只是获取context时有所不同。很简单的就能想到我们会在项目中对资源操作、IO操作等分装一个工具类,通过传递context来区分目标,这样能很好的简化复杂性。
分析这段代码,可看见程序A和B之间的联系有三个:
1 mainfest中声明shareuserId时需要知道一个共同的userId
2 createpackageContext时需要知道目标APK的package的name
3 获取资源时需要知道该资源的对应ID