七牛---Android SDK常见问题总结

很多用户在使用七牛中遇到各种Android SDK的问题,这里总结一些常见的问题:

1).遇到运行报错NoClassDefFound,这个其实是有些jar包没有正常导入,可以参考官网上面的需要的jar包依赖,另外也可以直接下载如下总结的依赖的jar包:

jar包依赖关系如下:
1.qiniu-android-sdk-7.2.0依赖okhttp3.0以上,依赖包如下:
http://7xsjg3.com1.z0.glb.clouddn.com/qiniu-android-sdk-7.2.0.jar
http://7xsjg3.com1.z0.glb.clouddn.com/okhttp-3.2.0.jar
http://7xsjg3.com1.z0.glb.clouddn.com/okio-1.6.0.jar
http://7xsjg3.com1.z0.glb.clouddn.com/happy-dns-0.2.3.2.jar

2.qiniu-android-sdk-7.1.0-7.1.2可以直接使用okhttp(2.6.0以上okhttp3.0以下),依赖包如下:
http://7xkn2v.dl1.z0.glb.clouddn.com/qiniu-android-sdk-7.1.0.jar
http://7xkn2v.dl1.z0.glb.clouddn.com/okhttp-2.7.0.jar
http://7xkn2v.dl1.z0.glb.clouddn.com/happy-dns-0.2.3.2.jar
http://7xkn2v.dl1.z0.glb.clouddn.com/okio-1.6.0.jar

3.qiniu-android-sdk-7.0.9 必须和 android-async-http-1.4.9 依赖,而 android-async-http-1.4.9 又必须依赖cz.msebera.android(即下载httpclient-4.4.1.1.jar),所以使用最新的 7.0.9Android 依赖的是 android-async-http-1.4.9.jar 和httpclient-4.4.1.1.jar 和 happy-dns.jar
http://7xl4c6.com2.z0.glb.qiniucdn.com/qiniu-android-sdk-7.0.9.jar
http://7xl4c6.com2.z0.glb.qiniucdn.com/android-async-http-1.4.9.jar
http://7xl4c6.com2.z0.glb.qiniucdn.com/httpclient-4.4.1.1.jar
http://7xl4c6.com2.z0.glb.qiniucdn.com/happy-dns-0.2.3.2.jar

4.qiniu-android-sdk-7.0.7~7.0.8 依赖的是 android-async-http-1.4.6~1.4.8,而 android-async-http-1.4.6~1.4.8 不需要依赖cz.msebera.android所以使用 qiniu-android-sdk-7.0.7~7.0.8 只需要导入 android-async-http-1.4.6~1.4.8 和 happy-dns.jar
http://7xl4c6.com2.z0.glb.qiniucdn.com/qiniu-android-sdk-7.0.7.2.jar
http://7xl4c6.com2.z0.glb.qiniucdn.com/android-async-http-1.4.8.jar
http://7xl4c6.com2.z0.glb.qiniucdn.com/happy-dns-0.2.3.2.jar

2).有关Android Studio以及Eclipse安装运行Android Demo步骤,这里以Android Studio为实例:

1.将以上相应的jar包也拷贝到工程的 libs 目录下,集成后的工程示例如下:
七牛---Android SDK常见问题总结_第1张图片

2.修改 build.gradle
双击打开您的工程目录下的 build.gradle,确保已经添加了对 相关jar包的依赖,如下所示:

dependencies {
     compile files('libs/happy-dns-0.2.3.2.jar')
    compile files('libs/okio-1.6.0.jar')
    compile files('libs/okhttp-2.7.0.jar')
    compile files('libs/qiniu-android-sdk-7.1.2.jar')
}

3.添加相关权限,在 app/src/main 目录中的 AndroidManifest.xml 中增加如下 uses-permission 声明

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

4.布局,在res/layout/activity_main.xml添加相应的上传的按钮以及相关控件,以下以一个简单的布局为例:

"wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:textSize="20sp"
        android:layout_marginTop="10dp"
        android:text="七牛云存储 SDK"
        android:id="@+id/textView"
        android:layout_alignParentTop="true"
        />

    

效果如下:
七牛---Android SDK常见问题总结_第2张图片

5.逻辑代码, 为按钮添加上传事件,这里以上传一个byte数组为例,另外,上传的数据可以是文件路径或者文件,以下给出具体的代码:

package com.dxy.cloud.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import org.json.JSONObject;

public class MainActivity extends Activity implements View.OnClickListener {

    //指定upToken, 强烈建议从服务端提供get请求获取, 这里为了掩饰直接指定key
    public static String uptoken = "um6IEH7mtwnwkGpjImD08JdxlvViuELhI4mFfoeL:NCnWTr1D3jR_9_dxj6geIgPBQq4=:eyJzY29wZSI6ImFuZHJvaWRkZW1vIiwiZGVhZGxpbmUiOjIzMDk5MjE5MTIsInJldHVybkJvZHkiOiJ7IFwiaGFzaFwiOiQoZXRhZyksXCJrZXlcIjokKGtleSksXCJtaW1lVHlwZVwiOiQobWltZVR5cGUpLCBcImZzaXplXCI6JChmc2l6ZSl9In0=";
    private Button btnUpload;
    private TextView textView;
    private UploadManager uploadManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
        btnUpload = (Button) findViewById(R.id.button);
        btnUpload.setOnClickListener(this);
        //new一个uploadManager类
        uploadManager = new UploadManager();

    }

    @Override
    public void onClick(View v) {
        Log.i("qiniutest", "starting......");
        byte[] data=new byte[]{ 0, 1, 2, 3};
        //设置上传后文件的key
        String upkey = "uploadtest.txt";
        uploadManager.put(data, upkey, uptoken, new UpCompletionHandler() {
            public void complete(String key, ResponseInfo rinfo, JSONObject response) {
                btnUpload.setVisibility(View.INVISIBLE);
                String s = key + ", " + rinfo + ", " + response;
                Log.i("qiniutest", s);
                textView.setTextSize(10);
                String o = textView.getText() + "\r\n\r\n";
                //显示上传后文件的url
                textView.setText(o + s + "\n" + "http://xm540.com1.z0.glb.clouddn.com/" + key);
            }
        }, new UploadOptions(null, "test-type", true, null, null));

    }
}

运行的效果图如下:
七牛---Android SDK常见问题总结_第3张图片

3).有关断点续传,暂停上传,设置自定义变量的用法,这里以一个简单的Demo给出下实现的方法,这里的Demo主要实现从相册选择一张图片上传来说明。

可以参考放GitHub(Android Studio)上的源码: https://github.com/clouddxy/AndroidDemo
这里直接给出MainActivity中的代码:

package com.dxy.cloud.myapplication;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.Configuration;
import com.qiniu.android.storage.KeyGenerator;
import com.qiniu.android.storage.Recorder;
import com.qiniu.android.storage.UpCancellationSignal;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UpProgressHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import com.qiniu.android.storage.persistent.FileRecorder;
import com.qiniu.android.utils.UrlSafeBase64;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;

public class MainActivity extends Activity {
    private Button button1;
    private Button button2;
    private Button button3;
    private ImageView imageview;
    private Uri imageUri;
    private TextView textview;
    private ProgressBar progressbar;
    public static final int RESULT_LOAD_IMAGE = 1;
    private volatile boolean isCancelled = false;
    UploadManager uploadManager;
    public MainActivity() {
        //断点上传
        String dirPath = "/storage/emulated/0/Download";
        Recorder recorder = null;
        try{
            File f = File.createTempFile("qiniu_xxxx", ".tmp");
            Log.d("qiniu", f.getAbsolutePath().toString());
            dirPath = f.getParent();
            //设置记录断点的文件的路径
            recorder = new FileRecorder(dirPath);
        } catch(Exception e) {
            e.printStackTrace();
        }

        final String dirPath1 = dirPath;
        //默认使用 key 的url_safe_base64编码字符串作为断点记录文件的文件名。
        //避免记录文件冲突(特别是key指定为null时),也可自定义文件名(下方为默认实现):
        KeyGenerator keyGen = new KeyGenerator(){
            public String gen(String key, File file){
                // 不必使用url_safe_base64转换,uploadManager内部会处理
                // 该返回值可替换为基于key、文件内容、上下文的其它信息生成的文件名
                String path = key + "_._" + new StringBuffer(file.getAbsolutePath()).reverse();
                Log.d("qiniu", path);
                File f = new File(dirPath1, UrlSafeBase64.encodeToString(path));
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new FileReader(f));
                    String tempString = null;
                    int line = 1;
                    try {
                        while ((tempString = reader.readLine()) != null) {
//                          System.out.println("line " + line + ": " + tempString);
                            Log.d("qiniu", "line " + line + ": " + tempString);
                            line++;
                        }

                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } finally {
                        try{
                            reader.close();
                        } catch(Exception e) {
                            e.printStackTrace();
                        }
                    }
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return path;
            }
        };

        Configuration config = new Configuration.Builder()
                // recorder 分片上传时,已上传片记录器
                // keyGen 分片上传时,生成标识符,用于片记录器区分是那个文件的上传记录
                .recorder(recorder, keyGen)
                .build();
        // 实例化一个上传的实例
        uploadManager = new UploadManager(config);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1 = (Button) findViewById(R.id.bt1);
        button2 = (Button) findViewById(R.id.bt2);
        button3 = (Button) findViewById(R.id.bt3);
        imageview = (ImageView) findViewById(R.id.iv);
        textview = (TextView) findViewById(R.id.tv);
        progressbar = (ProgressBar) findViewById(R.id.pb);

        // final String token = edittext.getText().toString();

        button1.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(
                        Intent.ACTION_PICK,
                        android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

                startActivityForResult(i, RESULT_LOAD_IMAGE);

            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK
                && data != null) {
            Uri selectedImage = data.getData();
            String[] filePathColumn = { MediaStore.Images.Media.DATA };

            Cursor cursor = getContentResolver().query(selectedImage,
                    filePathColumn, null, null, null);
            cursor.moveToFirst();

            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            final String picturePath = cursor.getString(columnIndex);
            Log.d("PICTUREPATH", picturePath);
            cursor.close();

            imageview.setVisibility(View.VISIBLE);
            imageview.setImageBitmap(BitmapFactory.decodeFile(picturePath));

            //自定义参数returnbody  
            //"returnBody":"{\"key\":$(key),\"hash\":$(etag),\"fname\":$(fname),\"phone\":$(x:phone)}
            final String token = "um6IEH7mtwnwkGpjImD08JdxlvViuELhI4mFfoeL:K3qVBauYrwu9iPOj1cVCfcHPXOk=:eyJzY29wZSI6ImphdmFkZW1vIiwicmV0dXJuQm9keSI6InsgXCJoYXNoXCI6JChldGFnKSxcImtleVwiOiQoa2V5KSxcIm1pbWVUeXBlXCI6JChtaW1lVHlwZSksIFwiZm5hbWVcIjokKGZuYW1lKSwgXCJwaG9uZVwiOiQoeDpwaG9uZSl9IiwiZGVhZGxpbmUiOjE4MjY5OTkxNTF9";
            button2.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {

                    //设定需要添加的自定义变量为Map类型 并且放到UploadOptions第一个参数里面
                    HashMap map = new HashMap();
                    map.put("x:phone", "12345678");

                    Log.d("qiniu", "click upload");
                    isCancelled = false;
                    uploadManager.put(picturePath, null, token,
                            new UpCompletionHandler() {
                                public void complete(String key,
                                                     ResponseInfo info, JSONObject res) {
                                    Log.i("qiniu", key + ",\r\n " + info
                                            + ",\r\n " + res);

                                    if(info.isOK()==true){
                                        textview.setText(res.toString());
                                    }
                                }
                            }, new UploadOptions(map, null, false,
                                    new UpProgressHandler() {
                                        public void progress(String key, double percent){
                                            Log.i("qiniu", key + ": " + percent);
                                            progressbar.setVisibility(View.VISIBLE);
                                            int progress = (int)(percent*1000);
//                                          Log.d("qiniu", progress+"");
                                            progressbar.setProgress(progress);
                                            if(progress==1000){
                                                progressbar.setVisibility(View.GONE);
                                            }
                                        }

                                    }, new UpCancellationSignal(){
                                @Override
                                public boolean isCancelled() {
                                    return isCancelled;
                                }
                            }));
                }
            });

            button3.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    isCancelled = true;
                }
            });
        }
    }
}

上传过程进度条:
七牛---Android SDK常见问题总结_第4张图片
上传后自定义变量回调:
七牛---Android SDK常见问题总结_第5张图片

4).关于从服务器获取Token上传:

Android SDK为客户端SDK,没有包含token生成实现,为了安全,token都建议通过网络从服务端获取,具体生成代码可以参考”java/python/php/ruby/go”等服务端sdk, 其实也比较简单,可以使用okhttp发送一个简单的get请求就可以了,这里以使用SyncHttpClient为例来说明:

package com.example.androidupload;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.loopj.android.http.SyncHttpClient;
import com.loopj.android.http.TextHttpResponseHandler;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UpProgressHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import com.qiniu.android.utils.UrlSafeBase64;
import org.apache.http.Header;
import org.json.JSONObject;
import java.io.File;
import java.util.UUID;

public class MainActivity extends Activity {

    private UploadManager uploadManager;
    private final String tag = "MainActivity";

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //设置本地上传的文件的路径
        String path ="/storage/emulated/0/Download/DSC_0770.JPG";
        String aaa = UrlSafeBase64.encodeToString(path);
        File file =new File(path);
    }

    private void upload(final String localPath,final String key)
    {
        Thread thread = new Thread()
        {
            @Override
            public void run() {
                super.run();
                SyncHttpClient client = new SyncHttpClient();
                //获取服务端的token的url
                String url = "http://xxxx/xxxx/xxx";
                client.get(url, new TextHttpResponseHandler() {
                    @Override
                    public void onFailure(int i, Header[] headers, String s, Throwable throwable) {
                    }
                    @Override
                    public void onSuccess(int i, Header[] headers, String s) {
                        Log.i(tag,"请求七牛token:"+s);
                        JsonParser jsonParser = new JsonParser();
                        JsonElement jsonElement =jsonParser.parse(s);
                        String token = jsonElement.getAsJsonObject().get("uptoken").toString();
                        Log.i(tag,"七牛开始上传"+localPath+"\n"+key+"\n"+token);
                        if(uploadManager==null)
                        {
                            uploadManager = new UploadManager();
                        }
                        UploadOptions uploadOptions = new UploadOptions(null, null, false,
                                new UpProgressHandler() {
                                    @Override
                                    public void progress(String key, double percent) {
                                        Log.i(tag,"a 七牛上传progress:"+percent+"\n"+key);
                                    }
                                }, null);

                        //调用uploadManager上传
                        uploadManager.put(localPath, key, token, new UpCompletionHandler() {
                            @Override
                            public void complete(String key, com.qiniu.android.http.ResponseInfo info, JSONObject response) {
                                Log.i(tag, "a 七牛上传complete:"+key + ",\r\n " + info + ",\r\n " + response);
                            }
                        },uploadOptions);
                    }
                });
            }
        };
        thread.start();
    }
}

5).关于for循环上传:

可以参考demo:

package com.dxy.cloud.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.qiniu.android.http.ResponseInfo;
import com.qiniu.android.storage.UpCompletionHandler;
import com.qiniu.android.storage.UploadManager;
import com.qiniu.android.storage.UploadOptions;
import org.json.JSONObject;

public class MainActivity extends Activity implements View.OnClickListener {

    //指定upToken, 强烈建议从服务端提供get请求获取, 这里为了掩饰直接指定key
    public static String uptoken = "um6IEH7mtwnwkGpjImD08JdxlvViuELhI4mFfoeL:NCnWTr1D3jR_9_dxj6geIgPBQq4=:eyJzY29wZSI6ImFuZHJvaWRkZW1vIiwiZGVhZGxpbmUiOjIzMDk5MjE5MTIsInJldHVybkJvZHkiOiJ7IFwiaGFzaFwiOiQoZXRhZyksXCJrZXlcIjokKGtleSksXCJtaW1lVHlwZVwiOiQobWltZVR5cGUpLCBcImZzaXplXCI6JChmc2l6ZSl9In0=";
    private Button btnUpload;
    private TextView textView;
    private UploadManager uploadManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
        btnUpload = (Button) findViewById(R.id.button);
        btnUpload.setOnClickListener(this);
        //new一个uploadManager类
        uploadManager = new UploadManager();
    }

    @Override
    public void onClick(View v) {
        Log.i("qiniutest", "starting......");
        byte[] data=new byte[]{ 0, 1, 2, 3, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3, 4, 5, 6,0, 1, 2, 3,};

       for(int i=0;inew UpCompletionHandler() {
                public void complete(String k, ResponseInfo rinfo, JSONObject response) {
                    String s = k + ", "+ rinfo + ", " + response;
                    Log.i("qiniutest", s);
                    String key = getKey(k, response);
                    String o = hint.getText() + "\r\n\r\n";
                    hint.setText(o + s + "http://xm540.com1.z0.glb.clouddn.com/" + key);
                }
            }, new UploadOptions(null, "test-type", true, null, null));
        }
    }
}

6).常见的返回的状态码:

使用七牛 Android sdk 的时候, 如果使用方式不正确,会返回一些对应的错误码。
具体可以参考源码:
https://github.com/qiniu/android-sdk/blob/c4cd1437aa1f2a0d68122e46b83580facdf1b74a/library/src/main/java/com/qiniu/android/http/ResponseInfo.java

public static final int ZeroSizeFile = -6;
public static final int InvalidToken = -5;
public static final int InvalidArgument = -4;
public static final int InvalidFile = -3;
public static final int Cancelled = -2;
public static final int NetworkError = -1;
public static final int TimedOut = -1001;
public static final int UnknownHost = -1003;
public static final int CannotConnectToHost = -1004;
public static final int NetworkConnectionLost = -1005;

7).关于混淆配置:

混淆处理 对七牛的SDK不需要做特殊混淆处理,混淆时将七牛相关的包都排除就可以了。

在Android Studio中,混淆配置在 proguard-rules.pro 里面加上下面几行混淆代码就行:

-keep class com.qiniu.**{*;}
-keep class com.qiniu.**{public <init>();}
-ignorewarnings

注意:-ignorewarnings这个也是必须加的,如果不加这个编译的时候可能是可以通过的,但是release的时候还是会出现错误。

对于Eeclipse中也是一样的,在proguard-project.txt 文件附件同样的排除代码就可以了

-keep class com.qiniu.**{*;}
-keep class com.qiniu.**{public <init>();}
-ignorewarnings

8).为什么进度会在95% 停很久:

因为上传进度是用sdk写入socket的 字节数/总字节数 作为进度,但写入socket不等于服务器收到并且处理完成,中间还有一段时间,如果只是用字节数就会出现更怪异的情况,在100% 停留很久,所以综合考虑就使用了 95%这个值.

9).华北地区上传域名需要修改:

华北地区的空间需要修改下上传域名:
https://support.qiniu.com/hc/kb/article/169006/?from=draft
不然会报错:
incorrect zone please use up-z1.qiniu.com

new uploadManager的时候传入这个配置修改上传的zone为zone1就可以了就可以了:
uploadManager = new UploadManager(new Configuration.Builder().zone(Zone.zone1).build());

你可能感兴趣的:(七牛)