这个问题从一开始接触到Android开发就困扰我很久了,平常除错少用中文log这个问题影响到不大,但是碰到需要把data (通常是远端的json or 本地端cache的sqlite)印出来观察这一种除错的情境时,这问题就头大了!
问了google也没有好解答,在android的google code里issue 1590就是在陈述这个问题,下面Comment提供的方法我试不出来,有趣的是用adb logcat在console下是不会有乱码的,所以问题一定出在ADT上,最近自已build了ADT trunk来用,刚好又遇到需要dump中文的data来debug的case,所以就尝试着自已来trace问题。
LogCat的相关的code都在LogPanel.java里,
public class LogPanel extends SelectionDependentPanel {
private LogCatOuputReceiver mCurrentLogCat;
@Override
protected Control createControl(Composite parent) {...}
private TabItem createTab(LogFilter filter, int index, boolean fillTable) {...}
/**
* Sent when a new device is selected. The new device can be accessed
* with {@link #getCurrentDevice()}.
*/
@Override
public void deviceSelected() {
startLogCat(getCurrentDevice());
}
public void startLogCat(final IDevice device) {
if (device == mCurrentLoggedDevice) {
return;
}
// if we have a logcat already running
if (mCurrentLoggedDevice != null) {
stopLogCat(false);
mCurrentLoggedDevice = null;
}
resetUI(false);
if (device != null) {
// create a new output receiver
mCurrentLogCat = new LogCatOuputReceiver();
// start the logcat in a different thread
new Thread("Logcat") { //$NON-NLS-1$
@Override
public void run() {
while (device.isOnline() == false &&
mCurrentLogCat != null &&
mCurrentLogCat.isCancelled == false) {
try {
sleep(2000);
} catch (InterruptedException e) {
return;
}
}
if (mCurrentLogCat == null || mCurrentLogCat.isCancelled) {
// logcat was stopped/cancelled before the device became ready.
return;
}
try {
mCurrentLoggedDevice = device;
device.executeShellCommand("logcat -v long", mCurrentLogCat, 0 /*timeout*/); //$NON-NLS-1$
} catch (Exception e) {
Log.e("Logcat", e);
} finally {
// at this point the command is terminated.
mCurrentLogCat = null;
mCurrentLoggedDevice = null;
}
}
}.start();
}
}
}
class Device的method executeShellCommand有三个参数分别是String command, IShellOutputReceiver receiver, int maxTimeToOutputResponse,它会透过class AdbHelper来做一下adb utility的指令操作。在第xx行时,执行了logcat -v long并把输出丢给LogCatOutputReceiver处理,所以我们的重点在于class LogCatOutputReceiver。继续追进去method executeShellCommand会发现它会把所有接收的data丢给interface IShellOutputReceiver的method addOutput处理,现在这个角色就是class LogCatOutputReceiver,而它的method addOutput是继承class MultiLineReceiver而来,问题就出在这里,把ISO -8859-1改成UTF-8就可以了...
public abstract class MultiLineReceiver implements IShellOutputReceiver {
/* (non-Javadoc)
* @see com.android.ddmlib.adb.IShellOutputReceiver#addOutput(
* byte[], int, int)
*/
public final void addOutput(byte[] data, int offset, int length) {
if (isCancelled() == false) {
String s = null;
try {
s = new String(data, offset, length, "ISO-8859-1"); //问题在这里,把所有输出的字串都使用ISO-8859-1 decode
} catch (UnsupportedEncodingException e) {
// normal encoding didn't work, try the default one
s = new String(data, offset,length);
}
// ok we've got a string
if (s != null) {
// if we had an unfinished line we add it.
if (mUnfinishedLine != null) {
s = mUnfinishedLine + s;
mUnfinishedLine = null;
}
// now we split the lines
mArray.clear();
int start = 0;
do {
int index = s.indexOf("\r\n", start); //$NON-NLS-1$
// if \r\n was not found, this is an unfinished line
// and we store it to be processed for the next packet
if (index == -1) {
mUnfinishedLine = s.substring(start);
break;
}
// so we found a \r\n;
// extract the line
String line = s.substring(start, index);
if (mTrimLines) {
line = line.trim();
}
mArray.add(line);
// move start to after the \r\n we found
start = index + 2;
} while (true);
if (mArray.size() > 0) {
// at this point we've split all the lines.
// make the array
String[] lines = mArray.toArray(new String[mArray.size()]);
// send it for final processing
processNewLines(lines);
}
}
}
}
}
然后重新编译DDMS即可。
$javac -classpath ./ddms.jar:./ddmlib.jar MultiLineReceiver.java
下面便为重新编译后的的ADT,其中除了中文问题,其他与官方的完全相同。