今天心情有些不爽, 原来之前内存溢出的原因不是手机硬件的问题, 是我代码写的有问题==
开始模拟的场景是这样的== 连后台取大JSON串,解析-转成相应对象-存入数据库; 由于后台暂时没有数据,所以只能自己模拟测试数据; 那么我的策略是这样的: 连续new出来目标数据的对象数放置到一个list中,然后再将这个list异步插入到数据库中;看似没有问题, 但是一直以来我都忽略了一个非常重要的问题,大数据! 万一有300W条记录呢? 你要new300W个对象吗? 开玩笑······· 所以这几天难为我的小米2了,一直认为它不给力, 不到100W就内存溢出·· 现在想起来, 其实已经很够给力的了;
这300W个对象的内存占用可不是闹着玩的··········· 所以我只能换种思路;
1. 我不通过new对象的形式向数据库中插入对象,反正我最终要的是模拟数据, 我直接用db.exel();循环向里面插入数据不也是一样的吗? 这样一来那300W个对象不就可以不用创建了吗? 为什么自己没早些考虑这方面的问题、 经验教训呐
2. 今天去咨询了兄弟开发IOS那边, 给了我几个比较有参考性的意见:
可以将每次提交的记录数减少==少量多次, 将300W条记录每1000条记录提交一次; 那么就可以缓解写入的数据库压力;
看看模拟的对象占用的内存是否已经将栈空间填满, 其实我考虑到运行时内存这个概念了,但是水平不够,没能往下思考;
在线解析JSON大字符串这个策略是不科学的,一方面耗时,其二万一中途网络中断,要考虑到断点续传?所以只能剩下其中2中方式可行:1,第一次安装的时候就先带一个数据库文件放置SD卡上,用程序读这个SD卡内容;或者在线下载一个压缩包,本地解压,然后在读;2,当后续有增量数据产生时,如果数据量小,可以采用在线解析JSON然后添加到数据库中; 如果数据量较大,考虑分批获取数据=少量多次请求数据; 如果间隔时间的确较长,参考后台给的数据压缩规则, 实在不行就重新获取新数据包,覆盖之前的数据;
3. 考虑了下, 我还是要模拟下从程序中读取sd卡中的数据库文件比较靠谱;用可视化工具做了个简单的数据库文件, 然后copy到SD卡中,开始模拟读取;
按照惯例, 先爆几张过程图看看流程==通过SQLite Developer可视化工具生成DB文件:
数据库文件已经拷贝到SD卡根目录下===
运行效果== 此处我只是做了查询处理,添加功能暂时未做,可根据上图结构自行添加:
说说以下测试程序的构成:
1. 傻瓜式布局;
2.从外界获取的数据库文件存放至SD卡的根目录;
3.manifest文件添加读写权限;
4.写一个openHelper数据库操作帮助类;
5.按照数据库表结构建立相应的实体类(entity or bean);
6.activity的数据展现;
【布局】
<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">
<EditText android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入要插入的数据"/>
<EditText android:id="@+id/input_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入数据的详细信息"/>
<Button android:id="@+id/addBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击添加"/>
<EditText android:id="@+id/showContext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="显示查询数据"/>
<Button android:id="@+id/searchBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击查询"/>
<TextView
android:id="@+id/show_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
【权限】
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
【openHelper帮助类】
package com.example.test_sqlite;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.example.test_sqlite.entity.MainData;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDBHelper extends SQLiteOpenHelper{
//数据库文件在SD卡中,此时必须用全限定名!! 默认路径在/data/data/databases/testDBinSD.sqlite, 但私密数据外界看不到-除非root。
private static final String SQL_NAME = "/sdcard/testDBinSD.sqlite";//数据库名称。//Environment.getExternalStorageDirectory().getPath() + "testDBinSD.sqlite";//
private static final String MAIN_DATA_TABLE_NAME = "maindata";//表名。
private static final String MAIN_DATA_ID = "id";//表的4个字段
private static final String MAIN_DATA_NAME = "name";
private static final String MAIN_DATA_SEX = "sex";
private static final String MAIN_DATA_GRADE = "grade";
//构造方法
public MyDBHelper(Context context) {
super(context, SQL_NAME, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//建表
String mainDataSQL = "create table if not exists " + MAIN_DATA_TABLE_NAME + "("
+ MAIN_DATA_ID + " varchar(20), "
+ MAIN_DATA_NAME + " varchar(20), "
+ MAIN_DATA_SEX + " varchar(20), "
+ MAIN_DATA_GRADE + " varchar(20));";
db.execSQL(mainDataSQL);
}
//读文件获取记录。
public List<MainData> getMainData() {
String mainDataSQL = "select * from "+MAIN_DATA_TABLE_NAME+"";
File name = new File(SQL_NAME);
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(name, null);//读SD卡数据库必须如此--用静态方法打开数据库。
Cursor cursor = db.rawQuery(mainDataSQL, null);
List<MainData> dataList = new ArrayList<MainData>();
if (cursor != null) {
while (cursor.moveToNext()) {//直到返回false说明表中到了数据末尾
MainData data = new MainData();
data.setId(cursor.getString(0));
data.setName(cursor.getString(1));
data.setSex(cursor.getString(2));
data.setGrade(cursor.getString(3));
dataList.add(data);
}
}
cursor.close();
db.close();
return dataList;
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
【实体类】
package com.example.test_sqlite.entity;
public class MainData {
private String id;
private String name;
private String sex;
private String grade;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
}
【activity数据查询-展现】
package com.example.test_sqlite;
import java.util.List;
import com.example.test_sqlite.entity.MainData;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.app.Activity;
public class MainActivity extends Activity implements OnClickListener {
private MyDBHelper dbHelper;//SQLite帮助类
private EditText input;
private EditText inputDetail;
private Button addBtn;
private EditText showContent;
//最主要功能是下面的俩=== 点击查询--数据呈现。
private Button searchBtn;
private TextView showText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDBHelper(this);
input = (EditText) findViewById(R.id.input);
inputDetail = (EditText) findViewById(R.id.input_detail);
addBtn = (Button) findViewById(R.id.addBtn);
showContent = (EditText) findViewById(R.id.showContext);
searchBtn = (Button) findViewById(R.id.searchBtn);
showText = (TextView) findViewById(R.id.show_textview);
addBtn.setOnClickListener(this);
searchBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.addBtn) {
// handleInsertDB();
} else if(v.getId() == R.id.searchBtn) {
handleSearchDB();
}
}
private void handleSearchDB() {
//查询sql。返回记录集合。
List<MainData> dataList = dbHelper.getMainData();
StringBuilder sb = new StringBuilder();
for(MainData data : dataList) {
sb.append(data.getId()).append("-").//循环将记录拼接起来方便显示。
append(data.getName()).append("-").append(data.getSex()).
append("-").append(data.getGrade()).append("-\n").toString();
}
showText.setText(sb);//显示结果。
}
private void handleInsertDB() {
}
}
说明从SD卡中读取SQLite文件是可以实现的============
那么同事给我的那些建议是可行的, 明天去公司试试, 哦不对, 今上午~
要睡觉了, 已经吵着基友睡觉了·························为了写博客,影响不少人啊、