目录
安卓应用
一、安卓存储-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" />