[android] 保存文件到手機內存


/*****************2016年5月4日 更新*******************************/

知乎:Android 沒有沙盒保護機制嗎,WhatsApp 信息為何可被隨意訪問?

pansz

Android 對每個應用程序定義了私有的存儲區域,這個區域通過 Linux 的文件系統權限控制,僅僅應用自己可以隨意讀寫,問其他應用無法訪問不屬於自己的私有數據。私有目錄的路徑可以通過 Context->getFilesDir() 來獲取。

 

除了私有存儲區域以外,SD 卡上都是公共區域,所有人可讀寫。

 

一個 app 選擇將隱私數據保存在公共區域,那是 App 選擇取向問題。與系統其實沒有什么關系。當然可以問 android 為什么要允許讀寫 SD 卡上任意目錄,個人覺得這是歷史問題,如果現在禁止了,估計一大堆讀寫 SD 卡的應用程序會出現兼容性問題,為了保證這種兼容性,感覺 android 不會將讀寫 SD 卡這種功能禁止掉。

 

Kifile

我覺得更應該是由於儲存空間的關系。 在以前,不是任何一台設備都擁有幾個g的系統儲存空間,他們很多都只有100~200m的位置來存放app文件。 android中私有文件放在/data/data/$pakage 中,但是/data屬於系統目錄,如果把文件儲存在里面,那勢必會減少存放app文件的空間,這是得不償失的。所以很多與系統文件無關的資源文件就只能放在sdcard中。 由於這種歷史原因,雖然很多最新版的設備已經大幅提升其系統儲存空間,但開發者們仍舊會將自身的資源文件放到sdcard中。 並且在最新的android4.4中,對於android程序的資源文件建議儲存在/sdcard/Android/$package 中,我覺得這是一個很好的進步,規范了文件的儲存位置,離它的訪問權限管理還會遠嗎?

 

知乎:為什么 Android 4.4 KitKat 限制第三方應用的 SD 卡讀寫權限?

pansz

就目前而言,第二 SD 卡仍然是可以讀寫的,只是要讀寫到指定的目錄(具體應該在 /Android/data/)。這樣的規定意味着應用程序只能對 SD 卡的指定目錄進行讀寫,不能讀寫任意目錄。相當於 Google 出手對 SD 卡目錄結構進行了規范。之前 android 不限制目錄,所以各種應用就隨意的在 SD 卡上建一個目錄。然后 SD 卡上的目錄到處都是,用戶對這種現象早就深惡痛絕了!如果 Google 對這件事情下狠手,只能說是大快人心。

 

另外說一下,SD 卡上的指定目錄是這樣獲取的:

1,程序相關的 內置存儲目錄,這個目錄位於內置 flash,應用程序可以隨意讀寫:

getFilesDir();

2,程序相關的 SD 卡外部存儲目錄,這個目錄位於 SD 卡,應用程序可以隨意讀寫:

getExternalFilesDir(null);

3,SD 卡公共目錄,這些目錄仍然可以訪問,不受權限限制:

Environment.getExternalStoragePublicDirectory(x)

其中 x 可以是 Environment.DIRECTORY_ALARMS 等預定義的常量。可以查找 Environment 的幫助。

 

如果大家要存儲數據,可以用 1 或者 2 的方法,獲取正確的目錄,然后進行任意讀寫,這樣不會把 SD 卡的目錄寫亂。

/*****************************************************************************/

 

 

1. 界面的准備工作,普通登錄界面,采用線性布局和相對布局。

<Checkbox/>有個屬性 android:checked=”true”,默認選中狀態,相對布局里面<Button/>位於右邊android:layout_alignParentRight=”true”,位於父控件的右面。密碼框星號顯示android:inputType=”textPassword”

 

2. 遇到device not found等錯誤可以直接忽略掉,布局文件屬性里面綁定點擊方法,傳入的參數View對象代表當前按鈕,控件首先都聲明在Activity的成員屬性里面,在onCreate()方法里面初始化,初始化控件一定要在setContentView()方法加載完界面之后才行。

 

3. 復選框判斷是否選中使用CheckBox對象的isChecked()方法,判斷字符串是否相等用String對象的equals()方法,logcat如果無法打印日志,關閉logcat重開或者關閉eclipse

 

4. 保存文件javaSE里面是直接new File(“aaa.txt”),文件默認保存在工程的目錄下面,但是在android系統里面,這樣默認是創建在/data/app 目錄下面,這里是不允許創建文件的。Android下每一個應用都有自己的數據文件夾/data/data/包名/

 

5. 新建一個業務類來處理保存信息的操作。這里的寫法和javaSE一樣,new File(“/data/data/包名/文件名”)對象,new FileOutputStream() 對象,此時會有異常拋出,因為我們這個方法有返回boolean值,所以我們捕獲掉,如果是無返回值那就throws Exception拋出去。字符串信息getBytes()轉成字節數組,調用foswrite()方法,關閉fos。當這個方法沒有使用類的成員屬性的時候,谷歌推薦把這個方法定義成static靜態的,效率更高

 

6. 文件路徑部分,如果按照上面所寫,靈活性很差。當我改變包名的時候,程序會報錯,R文件要從新導一下,並且android會認為是個新的應用。谷歌提供了一個api來獲取應用的數據目錄,調用Context上下文對象的getFilesDir()方法,返回的是/data/data/包名/files/。因此可以這樣new File(context.getFilesDir(),"info1.txt");來寫。

 

7. 數據目錄還有個文件夾是cache目錄,調用Context對象的getCacheDir()來獲取,這個目錄可以通過設置里面清除緩存清掉,這個目錄不能存放過大的文件

 

8. 上下文就是一個類提供了方便的api可以得到應有程序的環境,可以獲取包名,文件路徑,資源路徑,資產路徑等

 

9. 讀取保存文件的信息,同樣new File() ,new FileInputStream() ,

 

activity代碼:

 

package com.tsh.savefile;

import java.util.Map;

import org.w3c.dom.Text;

import com.tsh.savefile.service.LoginService;

import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
    private EditText et_username;
    private EditText et_password;
    private CheckBox cb_rember;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_username=(EditText) findViewById(R.id.et_username);
        et_password=(EditText) findViewById(R.id.et_password);
        cb_rember=(CheckBox) findViewById(R.id.cb_rember);
        //讀取
        Map<String, String> info=LoginService.getSavedUserInfo(this);
        if(info != null){
            et_username.setText(info.get("username"));
            et_password.setText(info.get("password"));
        }
    }
    /**
     * 登陸
     * @param v
     */
    public void login(View v){
        String username=et_username.getText().toString().trim();
        String password=et_password.getText().toString().trim();
        if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)){
            Toast.makeText(this, "用戶名和密碼不能為空", Toast.LENGTH_SHORT).show();
        }
        //記住密碼
        if(cb_rember.isChecked()){
            Boolean res=LoginService.saveUserInfo(this,username, password);
            if(res){
                Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(this, "保存失敗", Toast.LENGTH_SHORT).show();
            }
        }
        //驗證
        if(username.equals("taoshihan")&&password.equals("1")){
            Toast.makeText(this, "登陸成功", Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(this, "用戶名或密碼錯誤", Toast.LENGTH_SHORT).show();
        }
    }
}

 

業務類代碼:

package com.tsh.savefile.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;

public class LoginService {
    /**
     * 保存用戶名和方法的業務方法
     * @param context 上下文
     * @param username 用戶名
     * @param password 方法
     * @return
     */
    public static boolean saveUserInfo(Context context,String username,String password){
        File file=new File(context.getFilesDir(),"info1.txt");
        try {
            FileOutputStream fos=new FileOutputStream(file);
            String info=username+"##"+password;
            fos.write(info.getBytes());
            fos.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        
    }
    /**
     * 讀取
     * @return
     */
    public static Map<String,String> getSavedUserInfo(Context context){
        File file=new File(context.getFilesDir(),"info1.txt");
        try {
            FileInputStream fis=new FileInputStream(file);
            BufferedReader br=new BufferedReader(new InputStreamReader(fis));
            String[] res=br.readLine().split("##");
            Map<String, String> map=new HashMap<String,String>();
            map.put("username", res[0]);
            map.put("password", res[1]);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        
    }
}

layout代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.tsh.savefile.MainActivity" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="登錄名" />

    <EditText
        android:id="@+id/et_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="密碼" />

    <EditText
        android:id="@+id/et_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <CheckBox
            android:id="@+id/cb_rember"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:checked="true"
            android:text="記住密碼" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:onClick="login"
            android:text="登陸" />
    </RelativeLayout>

</LinearLayout>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM