(轉)Android-shareUserId作用

轉載: http://www.cnblogs.com/wotakuc/archive/2013/03/27/2984423.html

1.shareUserId介紹:

Android給每個APK進程分配一個單獨的空間,manifest中的userid就是對應一個分配的Linux用戶ID,
并且為它創(chuàng)建一個沙箱,以防止影響其他應用程序(或者其他應用程序影響它)。
用戶ID 在應用程序安裝到設備中時被分配,并且在這個設備中保持它的永久性。
通常,不同的APK會具有不同的userId,因此運行時屬于不同的進程中,而不同進程中的資源是不共享的,
在保障了程序運行的穩(wěn)定。然后在有些時候,我們自己開發(fā)了多個APK并且需要他們之間互相共享資源,
那么就需要通過設置shareUserId來實現這一目的。
通過Shared User id,擁有同一個User id的多個APK可以配置成運行在同一個進程中.所以默認就是可以互相訪問任意數據. 也可以配置成運行成不同的進程, 同時可以訪問其他APK的數據目錄下的數據庫和文件.就像訪問本程序的數據一樣。

2.shareUserId設置:

在需要共享資源的項目的每個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">

3.不同APP的(/data/data/app包名/文件)的共享

每個安裝的程序都會根據自己的包名在手機文件系統(tǒng)的/data/data/app包名/建立一個文件夾(需要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 {
            //此處調用并沒有區(qū)別,但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);
            //此處調用并沒有區(qū)別,但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。

4.Resources和SharedPreferences的共享

通過shareuserId共享,我們可獲取到程序A的context。因此,我們就可以通過context來獲取程序A對應的各種資源。
比較常用的就是Raw資源的獲取,如一些軟件的apk皮膚包就是采用了這種技術,將主程序和皮膚資源包分在兩個apk中。
獲取Resources很簡單,在程序A和B的mainfest中設置好相同的shareuserId后,通過createPackageContext獲取context即可。
之后就和原來的方式一樣,通過getResources函數獲取各種資源,只是此時的context環(huán)境是目標APP的context環(huán)境。

//在B中獲取A的各種資源
Context friendContext = this.createPackageContext( "com.example.shareusertesta", Context.CONTEXT_IGNORE_SECURITY);
Resources res = friendContext.getResources();
int xId = res.getIdentifier("xxx", "drawable", "com.example.shareusertesta"); //R.string.xxx 
int yId = res.getIdentifier("yyy", "string", "com.example.shareusertesta"); //R.Drawable.yyy
res.getString(xId);
res.getDrawable(yId);

5.訪問安全性(簽名)

上文中通過測試,驗證了同key下設置相同shareuserid后可共享資源,否則失敗。
但還有兩種情況尚未討論。一是假設A和C用兩個不同的簽名,但設置相同的shareuserid,那么能否共享資源。
二是假設A用簽名后的apk安裝,C用usb直連調試(即debug key),兩者設置相同的shareuserid,那么能否共享資源。
經過測試,不論是USB調試還是新簽名APK都安裝不上。
再進一步測試后發(fā)現,能否安裝取決于之前手機中是否已經存在對應該shareduserId的應用。
如有,則需要判斷簽名key是否相同,如不同則無法安裝。也就是說,如果你刪除a和b的應用先裝c,此時c的安裝正常,
而原來a和b的安裝就失敗了(a、b同key,c不同key,三者userId相同)。

6.其他討論

1 android:sharedUserId="android.uid.system" 如果這么設置,可實現提權的功能,修改系統(tǒng)時間等需要core權限的操作就可完成了。
但看到有人說會造成sd卡讀取bug,網上有不少解決方案(未測試)。

2 修改shareuserId后,usb開發(fā)調試安裝沒有問題,但是利用Ecplise打包簽名APK后,
部分機型會造成無法安裝的問題。網上有提到需要源碼環(huán)境mm打包或其他,較麻煩暫未驗證。
目前測試了三臺機子:三星S3自帶系統(tǒng)失??;華為一機子成功;三星一刷官方anroid系統(tǒng)的機子成功。
初步估計部分廠商修改了一定的內核,造成安裝失敗,具體兼容性情況有待進一步測試

3 使用shareuserid后,對同系列的產品的簽名key必須統(tǒng)一,不要丟失。否則后面開發(fā)的系列app就無法獲取數據了。
此外,注意從沒有userId的版本到有userId版本時的升級,也可能存在一定的安全權限問題。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容