天天看点

android棘手综合问题小记

代码只是解决特定问题的方法,并未深入研究每一个方法的原理,仅作为自身积累所用。如果对你有启发,请点赞哦。

1. 使用Gson解析自定义javabean

①.中括号开头的json

Type listType = new TypeToken<List<User>>(){}.getType(); 
Gson gson = new Gson(); 
List<User> users = gson.fromJson(jsonData, listType); 
           

②.大括号开头的json

Gson gson = new Gson(); 
User user = gson.fromJson(jsonData, User.class);
           

2. 使用httpclient上传图文混合信息给服务器,httpclient在android6.0系统上被移除,其它低版本系统上自带

public static String postFileList(List<File> fileList, String url) throws IOException {

        if(fileList==null||fileList.size()==)return null;

        FileBody bin = null;
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost(url);
        //请记住,这边传递汉字会出现乱码,解决方法如下,设置好编码格式就好
        StringBody username = new StringBody("136哈哈哈", Charset.forName("UTF-8"));
        MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null,
                Charset.forName("UTF-8"));
        for(File file :fileList){
            bin = new FileBody(file);
            String name=file.getName().substring(, file.getName().length()-);
            reqEntity.addPart(name, bin);
        }
        reqEntity.addPart("sid", username);
        httppost.setEntity(reqEntity);
        System.out.println("执行: " + httppost.getRequestLine());

        HttpResponse response = httpclient.execute(httppost);
        System.out.println("statusCode is " + response.getStatusLine().getStatusCode());
        HttpEntity resEntity = response.getEntity();
        System.out.println("----------------------------------------");
        System.out.println(response.getStatusLine());
        String result = null;
        if (resEntity != null) {
            System.out.println("返回长度: " + resEntity.getContentLength());
            System.out.println("返回类型: " + resEntity.getContentType());
        }
        int returnCode = response.getStatusLine().getStatusCode();
        if(returnCode==){
            //HttpEntity responseEntity = response.getEntity();
            result = EntityUtils.toString(resEntity);
            System.out.println(result);
            return result;
        }
        else {
            return null;
        }
    }
           

3. 使用Glide给视频文件的一帧图片附加播放图标

Glide使用方式:

Glide.with(context)
 .load(数据源)
 .into(targetImageView);
           

其中数据源类型可以是如何,很可惜没有Bitmap:

.load(String string) string可以为一个文件路径、uri或者url

.load(Uri uri) uri类型

.load(File file) 文件

.load(Integer resourceId) 资源Id,R.drawable.xxx或者R.mipmap.xxx

.load(byte[] model) byte[]类型

.load(T model) 自定义类型

那么问题来了,如何使用Glide加载两个Bitmap组合或者拼接后的Bitmap呢,找到一种方法可以解决,例如要给一个视频文件加一个播放的图标在其中间:

File imageFile = "xxx/xx.jpg";
Glide.with(mContext).load(imageFile).asBitmap().into(new SimpleTarget<Bitmap>() {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                Bitmap waterBitmap =((BitmapDrawable)mContext.getResources().getDrawable(R.mipmap.video_play)).getBitmap();
                //Bitmap拼接函数不再展开,此处是在图片上添加了居中播放的图标
                Bitmap bm = BitmapUtil.Watermark(resource,waterBitmap,);
                imageView.setImageBitmap(bm);
            }
        });
           

4. 布局中的组件被渲染之前getWidth()和getMeasuredWidth()都是0,却要使用其大小做一些计算。

假如有两个View,在没有渲染出来之前代码计算大小,两个的宽度都要设定为两个View中较大的一个,其核心是在组件还没有被展示之前得到其大小。getMeasuredWidth()是measure之后有值,而getWidth是在layout执行之后才有值,故手动调用measure测量一下大小,就能用getMeasuredWidth()获取值了。

TextView nameView = (TextView) linearLayout.findViewById(R.id.recognizerresult_name);
TextView ageView = (TextView) linearLayout.findViewById(R.id.recognizerresult_age);
 nameView.measure(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);       
ageView.measure(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
int maxWidth = Math.max(nameView.getMeasuredWidth(),ageView.getMeasuredWidth());
nameView.getLayoutParams().width = maxWidth;
ageView.getLayoutParams().width = maxWidth;
           

5. onPreviewFrame使用子线程解析每一帧数据,以及避免读写同时操作导致闪退

做人脸检测时或者二维码扫描,为了避免ui线程阻塞,肯定是要把检测人脸处理和耗时的计算操作放入子线程执行的,放入子线程就会有很多数据同步上的问题,此处我参考了opencv的JavaCameraView的代码,在此记录一下。

刚开始一直报如下错误,且运行过程中没有任何提示闪退,其中2255是处理每一帧数据的子线程id,遇到此类错误可以考虑是多线程同时读写或者数据不同步导致的。

- :: -/com.seengene.launcher A/libc: Fatal signal  (SIGSEGV), code , fault addr  in tid  (engene.launcher)
- :: -/com.seengene.launcher A/libc: Fatal signal  (SIGSEGV), code , fault addr  in tid  (Thread-)
           

以下是整理简化过的JavaCameraView部分代码,只作为记录原理用,不是实际代码。

//**这是一个长度为2的数组,之所以设计了这个,是为了避免onpreviewFrame所在线程和处理每一帧数据的线程同时读写同一个Mat数据
    //试想如果只是一个Mat那么肯定存在两个线程同时读写的情况,会导致程序运行过程中直接闪退**。
    private Mat[] mFrameChain;
    private int mChainIdx = ;
    //省略无关代码且有改动简化
     mFrameChain= new Mat[];
    mFrameChain[] = new Mat();
     mFrameChain[] = new Mat();
    }
    public void onPreviewFrame(byte[] frame, Camera arg1) {
    //通过对象锁的机制叫醒等待的人脸检测线程,当然如果人脸检测线程还没有在await状态,notify也是没有用的
        synchronized (this) {
            mFrameChain[mChainIdx].put(, , frame);
            mCameraFrameReady = true;
            this.notify();
        }
        //省略无关代码
    }
    //关闭相机之前一定要notify处理每一帧数据的子线程,不然会导致程序erro闪退
    protected void disconnectCamera() {
        /* 1. We need to stop thread which updating the frames
         * 2. Stop camera and release it
         */
        try {
            //先notify处理每一帧数据的子线程
            mStopThread = true;
            Log.d(TAG, "Notify thread");
            synchronized (this) {
                this.notify();
            }
            Log.d(TAG, "Wating for thread");
            if (mThread != null)
                mThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            mThread =  null;
        }
        //然后释放相机
        /* Now release camera */
        releaseCamera();

        mCameraFrameReady = false;
    }
    //处理每一帧数据,检测人脸、画人脸框的子线程
     private class CameraWorker implements Runnable {

        @Override
        public void run() {
            do {
                boolean hasFrame = false;
                synchronized (JavaCameraView.this) {
                    try {
                        while (!mCameraFrameReady && !mStopThread) {
                            JavaCameraView.this.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (mCameraFrameReady)
                    {
                        //在此处要把mChainIdx变化一下,这样下次onpreviewFrame中操作的是另外一个Mat了,这样我们这个线程就可以安心的用当前的这帧的Mat了
                        mChainIdx =  - mChainIdx;
                        mCameraFrameReady = false;
                        hasFrame = true;
                    }
                }
                if (!mStopThread && hasFrame) {
                    //因为mChainIdx已经变了,故要1-mChainIdx才是当前这帧的Mat数据
                    if (!mFrameChain[ - mChainIdx].empty())
                        deliverAndDrawFrame(mCameraFrame[ - mChainIdx]);
                }
            } while (!mStopThread);
        }
    }