第八章:android应用

安卓应用

一、安卓存储-sqllite数据库

1、使用sql语句对数据库进行增删改查
  • MainActivity
public class MainActivity extends AppCompatActivity {
    private MyOpenHelper myOpenHelper;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /**
         * 当数据库不存在时,SQLiteOpenHelper实例调用getWritableDatabase()或getReadableDatabase()会创建数据库,
         * 当数据库存在时则返回数据库实例(SQLiteDatabase)。
         */
        // 创建位置/data/data/com.wuxi.myandroid/databases/
        myOpenHelper = new MyOpenHelper(getApplicationContext());
        tv = findViewById(R.id.tv);
    }

    public void insertHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getWritableDatabase();
        db.execSQL("insert into person(name,age) values(?,?)", new Object[]{"孟美岐", 18});
        db.close();
    }

    public void deleteHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getWritableDatabase();
        db.execSQL("delete from person where name=?", new Object[]{"孟美岐"});
        db.close();
    }

    public void updateHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getWritableDatabase();
        db.execSQL("update person set age=? where name=?", new Object[]{19, "孟美岐"});
        db.close();
    }

    public void selectHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select * from person", null);
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        if (cursor != null && cursor.getCount() > 0) {
            boolean first = true;
            while (cursor.moveToNext()) {
                if (first) {
                    first = false;
                    sb.append("{id:");
                } else {
                    sb.append(",{id:");
                }
                sb.append(cursor.getInt(0))
                        .append(",name:").append(cursor.getString(1))
                        .append(",age:").append(cursor.getInt(2)).append("}");
            }
        }
        sb.append("]");
        tv.setText(sb);
        db.close();
    }
}
  • MyOpenHelper
public class MyOpenHelper extends SQLiteOpenHelper {
    /**
     * super()参数2:为数据库名称
     * super()参数4:为数据库版本,修改此参数后,会执行onUpgrade()
     */
    public MyOpenHelper(Context context) {
        super(context, "wuxi.db", null, 2);
    }

    /**
     * 只在数据库创建时调用一次,适合做表结构初始化操作
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table person(_id integer primary key autoincrement,name varchar(20))");
    }

    /**
     * 修改数据库版本后会执行此方法,适合做修改数据库表结构操作
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("alter table person add age integer");
    }
}
2、使用谷歌封装好的api对数据库增删改查
public class MainActivity extends AppCompatActivity {
    private MyOpenHelper myOpenHelper;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myOpenHelper = new MyOpenHelper(getApplicationContext());
        tv = findViewById(R.id.tv);
    }

    public void insertHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("name", "黄婷婷");
        values.put("age", 20);
        // 返回值为插入数据行的id
        long id = db.insert("person", null, values);
        Log.e("", String.valueOf(id));
        db.close();
    }

    public void deleteHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getWritableDatabase();
        // 返回值为删除的行数
        int num = db.delete("person", "name=?", new String[]{"黄婷婷"});
        Log.e("", String.valueOf(num));
        db.close();
    }

    public void updateHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("age", 21);
        // 返回值为修改的行数
        int num = db.update("person", values, "name=?", new String[]{"黄婷婷"});
        Log.e("", String.valueOf(num));
        db.close();
    }

    public void selectHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getReadableDatabase();
        // 参数2为null:查询所有字段;
        // 参数3和4为null:查询所有数据;
        Cursor cursor = db.query("person", new String[]{"_id", "name", "age"}, "name=?", new String[]{"黄婷婷"}, null, null, null);
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        if (cursor != null && cursor.getCount() > 0) {
            boolean first = true;
            while (cursor.moveToNext()) {
                if (first) {
                    first = false;
                    sb.append("{id:");
                } else {
                    sb.append(",{id:");
                }
                sb.append(cursor.getInt(0))
                        .append(",name:").append(cursor.getString(1))
                        .append(",age:").append(cursor.getInt(2)).append("}");
            }
        }
        sb.append("]");
        tv.setText(sb);
        db.close();
    }
}
3、Android中数据库事务的介绍
public class MainActivity extends AppCompatActivity {
    private MyOpenHelper myOpenHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myOpenHelper = new MyOpenHelper(getApplicationContext());
    }

    public void insertHandler(View view) {
        SQLiteDatabase db = myOpenHelper.getWritableDatabase();
        db.beginTransaction();//开启事务
        try {
            db.execSQL("update person set age=? where name=?", new Object[]{20, "孟美岐"});
            int i = 10 / 0;
            db.execSQL("update person set age=? where name=?", new Object[]{21, "黄婷婷"});
            db.setTransactionSuccessful();//事务成功标记
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            db.endTransaction();//根据成功标记决定是否回滚,并关闭事务
        }
        db.close();
    }
}

二、HttpURLConnection

1、请求、响应和更新视图
  • 添加权限
<uses-permission android:name="android.permission.INTERNET" />
  • MainActivity
public class MainActivity extends AppCompatActivity {
    private EditText et;
    private TextView tv;
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            // 1、Toast也必须在主线程绘制UI;2、可根据what属性判断状态。
            switch (msg.what) {
                case 200:
                    String content = (String) msg.obj;
                    tv.setText(content);
                    break;
                case 400:
                    Toast.makeText(getApplicationContext(), (String) msg.obj, Toast.LENGTH_SHORT).show();
                    break;
            }
            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et = findViewById(R.id.et);
        tv = findViewById(R.id.tv);
    }

    /**
     * 1、不能在主线程发送请求
     * 2、只有在主线程才能更新UI
     */
    public void search(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    /**
                     * POST请求方式:
                     * String path = "https://mock.mengxuegu.com/mock/604ac67cf340b05bceda3ee2/sso/auth/login";
                     * // 解决乱码问题
                     * String data = "username=" + URLEncoder.encode("用户名", "utf-8") + "&password=" + URLEncoder.encode("密码", "utf-8");
                     * URL url = new URL(path);
                     * HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                     * conn.setRequestMethod("POST");//要求大写,默认就是GET
                     * conn.setConnectTimeout(5000);
                     * conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                     * conn.setRequestProperty("Content-Length", data.length() + "");
                     * conn.setDoOutput(true);
                     * conn.getOutputStream().write(data.getBytes());
                     * int code = conn.getResponseCode();
                     */
                    String path = et.getText().toString().trim();
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");//要求大写,默认就是GET
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        String content = StreamUtils.readStream(in);
                        Message msg = Message.obtain();// 从消息池获取对象,效率比new Message()高
                        msg.what = 200;
                        msg.obj = content;
                        handler.sendMessage(msg);
                    }
                } catch (Exception e) {
                    Message msg = Message.obtain();
                    msg.what = 400;
                    msg.obj = e.getMessage();
                    handler.sendMessage(msg);
                }
            }
        }).start();
    }
}
  • activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/et"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:singleLine="true"
            android:text="https://www.baidu.com" />

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="search"
            android:text="搜索" />
    </LinearLayout>

    <!--ScrollView只能有一个子节点-->
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>
</LinearLayout>
  • StreamUtils
public class StreamUtils {
    public static String readStream(InputStream in) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = in.read(bytes)) != -1) {
            baos.write(bytes, 0, len);
        }
        in.close();
        return baos.toString();
    }
}
2、处理图片资源和资源缓存
public class MainActivity extends AppCompatActivity {
    private EditText et;
    private ImageView iv;
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            Bitmap bitmap = (Bitmap) msg.obj;
            iv.setImageBitmap(bitmap);
            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et = findViewById(R.id.et);
        iv = findViewById(R.id.iv);
    }

    public void search(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    /**
                     * 1、getCacheDir()目录地址:/data/data/包名/cache;
                     * 2、用户可通过清空缓存方式清空此目录数据
                     */
                    String path = et.getText().toString().trim();
                    // UUID.nameUUIDFromBytes(byte[]):参数一致,输出的UUID也一致
                    String filename = String.valueOf(UUID.nameUUIDFromBytes(path.getBytes()));
                    Log.e("红色日志--->", filename);
                    // 文件名过长会报错
                    File file = new File(getCacheDir(), filename);
                    if (file.exists() && file.length() > 0) {
                        Log.e("红色日志--->", "使用缓存图片");
                        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                        Message msg = Message.obtain();
                        msg.obj = bitmap;
                        handler.sendMessage(msg);
                    } else {
                        Log.e("红色日志--->", "第一次访问连接网络");
                        URL url = new URL(path);
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        conn.setRequestMethod("GET");
                        conn.setConnectTimeout(5000);
                        int code = conn.getResponseCode();
                        if (code == 200) {
                            InputStream in = conn.getInputStream();
                            FileOutputStream fos = new FileOutputStream(file);
                            int len = 0;
                            byte[] bytes = new byte[1024];
                            while ((len = in.read(bytes)) != -1) {
                                fos.write(bytes, 0, len);
                            }
                            fos.close();
                            in.close();
                            Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                            Message msg = Message.obtain();
                            msg.obj = bitmap;
                            handler.sendMessage(msg);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
3、runOnUiThread
public class MainActivity extends AppCompatActivity {
    private EditText et;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et = findViewById(R.id.et);
        tv = findViewById(R.id.tv);
    }

    public void search(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String path = et.getText().toString().trim();
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        int len = 0;
                        byte[] bytes = new byte[1024];
                        while ((len = in.read(bytes)) != -1) {
                            baos.write(bytes, 0, len);
                        }
                        in.close();
                        // 不管在什么位置调用都将运行在UI线程里
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                tv.setText(baos.toString());
                            }
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
4、其他常见消息api
public class MainActivity extends AppCompatActivity {
    private TextView tv;
    private Timer timer;
    private TimerTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv = findViewById(R.id.tv);

        /*
        // 3秒后执行run方法
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                tv.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            }
        }, 3000);
        */

        timer = new Timer();
        task = new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                    }
                });
            }
        };
        // 3秒后,每隔1秒执行一次run方法
        timer.schedule(task, 3000, 1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        timer.cancel();
        task.cancel();
    }
}
5、多线程下载和断点续传
  • 权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • MainActivity
public class MainActivity extends AppCompatActivity {
    private String path = "https://www.dingcaiyan.com/lantern-installer.exe";
    private final int threadCount = 3;// 下载开启的线程
    private int runningThread;// 代表当前正在运行的线程
    private LinearLayout ll;
    private ArrayList<ProgressBar> pbList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ll = findViewById(R.id.ll);
        pbList = new ArrayList<ProgressBar>();
    }

    private class DownLoadThread extends Thread {
        private int startIndex;
        private int endIndex;
        private int threadId;
        private int pbMaxSize;// 进度条最大值
        private int pbLastPosition;// 进度条断点位置

        public DownLoadThread(int startIndex, int endIndex, int threadId) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadId = threadId;
        }

        @Override
        public void run() {
            try {
                pbMaxSize = endIndex - startIndex;
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                /**
                 * 实现断点续传逻辑
                 */
                // a、获取已下载的大小信息
                File file = new File(getFilename(path) + threadId + ".txt");
                if (file.exists() && file.length() > 0) {
                    FileInputStream fis = new FileInputStream(file);
                    BufferedReader bufr = new BufferedReader(new InputStreamReader(fis));
                    int lastPosition = Integer.parseInt(bufr.readLine());
                    pbLastPosition = lastPosition - startIndex;
                    startIndex = lastPosition + 1;
                    fis.close();
                }
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
                int code = conn.getResponseCode();
                // 206(http状态码):服务器已经成功处理了部分GET请求,该请求必须包含Range头信息来指示客户端希望得到的内容范围,并且可能包含If-Range来作为请求条件。
                if (code == 206) {
                    RandomAccessFile raf = new RandomAccessFile(getFilename(path), "rw");
                    raf.seek(startIndex);
                    InputStream in = conn.getInputStream();
                    byte[] bytes = new byte[1024 * 1024];
                    int len = 0;
                    // b、存储已下载的大小信息
                    int total = 0;
                    while ((len = in.read(bytes)) != -1) {
                        raf.write(bytes, 0, len);
                        total += len;
                        int currentThreadPosition = startIndex + total;
                        // rwd模式:要求对文件内容的每个更新都同步写入到底层存储设备
                        RandomAccessFile rafPosition = new RandomAccessFile(getFilename(path) + threadId + ".txt", "rwd");
                        rafPosition.write(String.valueOf(currentThreadPosition).getBytes());
                        rafPosition.close();
                        // 与进度相关的控件都可以在子线程直接更新UI
                        pbList.get(threadId).setMax(pbMaxSize);
                        pbList.get(threadId).setProgress(pbLastPosition + total);
                    }
                    raf.close();
                    // c、删除存储的信息
                    synchronized (DownLoadThread.class) {
                        runningThread--;
                        if (runningThread == 0) {
                            for (int i = 0; i < threadCount; i++) {
                                File deleteFile = new File(getFilename(path) + i + ".txt");
                                deleteFile.delete();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void submit(View view) {
        ll.removeAllViews();
        pbList.clear();
        for (int i = 0; i < threadCount; i++) {
            ProgressBar progressbar = (ProgressBar) View.inflate(getApplicationContext(), R.layout.progressbar_item, null);
            pbList.add(progressbar);
            ll.addView(progressbar);
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    /**
                     * 实现多线程下载逻辑
                     */
                    // 1、获取网络连接
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        runningThread = threadCount;
                        // 2、本地磁盘创建相同大小的空文件
                        int fileSize = conn.getContentLength();
                        RandomAccessFile raf = new RandomAccessFile(getFilename(path), "rw");
                        raf.setLength(fileSize);
                        // 3、计算每条线程需从文件哪个部分开始下载,结束
                        int blockSize = fileSize / threadCount;
                        for (int i = 0; i < threadCount; i++) {
                            int startIndex = i * blockSize;
                            int endIndex;
                            if (i == threadCount - 1) {
                                endIndex = fileSize - 1;
                            } else {
                                endIndex = (i + 1) * blockSize - 1;
                            }
                            // 4、依次创建,启动多条线程来下载网络资源的指定部分
                            DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
                            downLoadThread.start();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public String getFilename(String path) {
        int start = path.lastIndexOf("/") + 1;
        String substring = path.substring(start);
        return Environment.getExternalStorageDirectory().getPath() + "/" + substring;
    }
}
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="submit"
        android:text="下载" />

    <LinearLayout
        android:id="@+id/ll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" />

</LinearLayout>
  • progressbar_item.xml
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/progressBar"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
原文地址:https://www.cnblogs.com/linding/p/15739686.html