最近的需求要求我的APP开放一些属性数据给其他APP访问,但不能允许其他APP修改我的数据。 在选择实现方式时,对比了content provider和sharepreference,其中,content provider的数据以数据表的形式存在,可允许外部应用进行增删改查,而sharepreference的数据以xml键值对的形式存在,可使用不同的MODE对外部应用的读写权限进行控制,写法简单。因此,最后选择使用sharepreference实现这个功能。
开始测试前,附上sharepreference的几种MODE:
MODE_PRIVATE 指定该sharepreferences数据只能被本应用程序读写MODE_WORLD_READABLE 指定该sharepreference数据对其他应用程序可读但不可写MODE_WORLD_WRITEABLE 指定该sharepreference数据对其他应用程序可读可写首先测试这个功能需要两个APP,一个负责写,另一个负责读,我的开发环境是Android studio,简单的建两个module即可。
APP1有四个按钮,下面这段代码是OnClickListener的内容:
switch (v.getId()) { case R.id.save: Log.i(TAG, "click btn save"); SharedPreferences sharedPreferences = getSharedPreferences(SP_NAME, MODE_WORLD_READABLE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("key1", "value1"); editor.putString("key2", "value2"); editor.commit(); Log.i(TAG, "save sp, over"); break; case R.id.read: Log.i(TAG, "click btn read"); Log.i(TAG, "read sp, mode world readable"); sharedPreferences = getSharedPreferences(SP_NAME, MODE_WORLD_READABLE); Log.i(TAG, "key1 " + sharedPreferences.getString("key1", "no-key1")); Log.i(TAG, "key2 " + sharedPreferences.getString("key2", "no-key2")); Log.i(TAG, "key3 " + sharedPreferences.getString("key3", "no-key3")); Log.i(TAG, "read sp, over"); break; case R.id.clear1: Log.i(TAG, "click btn clear1"); sharedPreferences = getSharedPreferences(SP_NAME, MODE_WORLD_READABLE); editor = sharedPreferences.edit(); editor.remove("key1"); editor.putString("key2", ""); editor.commit(); Log.i(TAG, "clear 1 over"); break; case R.id.clear2: Log.i(TAG, "click btn clear1"); sharedPreferences = getSharedPreferences(SP_NAME, MODE_WORLD_READABLE); editor = sharedPreferences.edit(); editor.clear(); editor.commit(); Log.i(TAG, "clear 2 over"); break; default: break; }APP2有一个按钮,只做读取APP1给出内容的操作,OnClickListener内容如下:
Context context = null; switch (v.getId()) { case R.id.read: try { context = createPackageContext("com.point.sharepreferencessource", Context.CONTEXT_IGNORE_SECURITY);//com.point.sharepreferencessource是APP1的包名 Log.i(TAG,"sp context create over"); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } if (context != null) { SharedPreferences sharedPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_WORLD_READABLE); Log.i(TAG, "read sp, mode world readable"); Log.i(TAG, "key1 " + sharedPreferences.getString("key1", "no-key1")); Log.i(TAG, "key2 " + sharedPreferences.getString("key2", "no-key2")); Log.i(TAG, "key3 " + sharedPreferences.getString("key3", "no-key3")); Log.i(TAG, "read sp, over"); } break; default: break; }这种方式不需要在manifest中添加任何权限。
代码中变量SP_NAME在APP1和APP2中必须相同。TAG可自定义,方便logcat过滤。
APP2中访问APP1的数据,需要知道APP1中存储sharepreference的文件名、key值。实际应用中需要给外部APP提供的接口文档应当写出这些内容。
sharepreference删除单个数据使用edit.remove(key);的方法,删除全部数据可以使用edit.clear();,而使用edit.putString(key,"");会将这个key的value设置为空串,而不是删除。
在APP2中读取的时候发现,下一次读取需要彻底退出APP2,才能正确的读出APP1最新的数据,但APP1可以常驻后台不杀。这里确认每次点击事件中try-catch代码片都重新执行了,但不退出而是放在后台的话,读出的数据是过时的,原因未知。实际对我的需求影响不大,没有继续查。有大神了解原因的请评论,万分感谢。
另外,需要注意的一点是,sharepreference一个文件只能指定一个MODE,如果同时对一个文件进行过private访问和world readable访问,那么这个文件将被private。测试中在APP1里对同一个sharepreference文件名get过这两种之后,在APP2里将会读取不到这个sharepreference文件里的数据,报错为Attempt to read preferences file /data/data/com.***.xml without permission,试图访问一个没有读取权限的文件。
好像写完了。
顺便发现了switch case的一个东西。 在APP1中那段switch case代码中,只有第一个case里面声明了SharePreference sharePreference = ***,在第二个case里面不能再声明,而要直接使用sharePreference = ***这个变量,认为switch在进行条件的比对时会把到匹配的case之前的代码片中声明过的变量全部声明一遍。这种写法理论上不规范,但是实际上可行。
